package tests

case class SimpleDescription(value: String) extends Description {
  override def toString = value
}

object BlockStatement {
  type CodeBlock = ()=> Any
  val DoNothing: CodeBlock = () => {}
}

abstract class BlockStatement[A <: Statement](description: String) extends Statement {

  import BlockStatement._

  protected var codeBlock: CodeBlock = DoNothing

  def apply(block: => Any): A = {
    codeBlock = () => { block }
    this.asInstanceOf[A]
  }

  def apply = {
    codeBlock()
  }

  def describe = SimpleDescription(description)

}

case class BeforeStatement(description: String) extends BlockStatement[BeforeStatement](description)

case class AfterStatement(description: String) extends BlockStatement[AfterStatement](description)

class TestStatement(var description: String) extends BlockStatement[TestStatement](description) with TestLifeCycle {

  private var beforesList: List[Statement] = Nil
  private var aftersList: List[Statement] = Nil
  private var skipped = false
  private var skipReasonDesc: String = null

  def execute = {
    codeBlock()
  }

  def befores = beforesList

  def afters = aftersList

  def skip(reason: String) = {
    skipReasonDesc = reason
    skipped = true
    this
  }

  def skip(reason: TestStatement) = {
    skipReasonDesc = description
    skipped = true
    description = reason.description
    this
  }

  def wasSkipped = skipped

  def skipReason = skipReasonDesc

  def timeout(timeout: Long) = TimedOutTestStatement(description, timeout)

}

case class TimedOutTestStatement(desc: String, timeout: Long) extends TestStatement(desc) with TestLifeCycle {

  import java.util.concurrent.{Callable, ExecutionException, ExecutorService, 
                               Executors, Future, TimeUnit, TimeoutException}

  private var timeunit = TimeUnit.MILLISECONDS

  override def execute = {
    val service = Executors.newSingleThreadExecutor
    val callable: Callable[Any] = () => {codeBlock()}
    val result = service submit callable
    service.shutdown();
    try {
      val terminated= service.awaitTermination(timeout, timeunit)
      if (!terminated) {
        service.shutdownNow
      }
      result.get(0, timeunit)
    } catch {
      case e: TimeoutException => error("test timed out after " + timeout + " milliseconds")
      case e: ExecutionException => throw unwrap(e)
    }
  }

  implicit def block2Callable(block: => Any) = new Callable[Any] {
    def call: Any =
      try {
        block
      } catch {
        case e: Throwable => new ExecutionException(e)
      }
  }

  private def unwrap(e: Throwable): Throwable =
    if (e.getClass.isAssignableFrom(classOf[ExecutionException])) unwrap(e.getCause()) else e

  def nanoseconds = {
    timeunit = TimeUnit.NANOSECONDS
    this
  }

  def microseconds = {
    timeunit = TimeUnit.MICROSECONDS
    this
  }

  def milliseconds = {
    timeunit = TimeUnit.MILLISECONDS
    this
  }

  def seconds = {
    timeunit = TimeUnit.SECONDS
    this
  }

  def minutes = {
    timeunit = TimeUnit.MINUTES
    this
  }

  def hours = {
    timeunit = TimeUnit.HOURS
    this
  }

  def ns = nanoseconds
  def ys = microseconds
  def ms = milliseconds
  def s = seconds
  def min = minutes
  def h = hours

}

object TestsImpl extends Application {

  implicit def stringWrapper(description: String) = new TestStatement(description)

  def before(description: String) = BeforeStatement(description)
  def after(description: String) = AfterStatement(description)
  def test(description: String) = new TestStatement(description)

  def test(description: String, timeout: Long) = TimedOutTestStatement(description, timeout)

  def skip(description: String) = {
    val test = new TestStatement(description)
    test skip description
    test
  }

  val b1 = before("null") {
    println("ok")
  }

  test("null") {
    println("ok")
  }.skip("sis")
 
  this skip "test description" timeout 10 s {
    
  }

  "because not implemented yet" skip "calcule sqrt" timeout 10 s {
    
  }
  
  "test description" timeout 10 s {
    
  }

  val test1 = "test 3 greater than 1" {
    println(3 > 1)
  }.skip("kosks")

  test1()

  after("null") {
    println("ok")
  }
}
// vim: set ts=4 sw=4 et:
